package org.framework.common.api.service.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.framework.common.api.entity.CommonEntityAware;
import org.framework.common.api.query.Criteria;
import org.framework.common.api.query.Criterion.Operator;
import org.framework.common.api.query.GroupBean;
import org.framework.common.api.query.OrderBean;
import org.framework.common.api.query.QueryBean;
import org.framework.common.api.query.Restrictions;
import org.framework.common.api.support.annotation.Search;
import org.framework.common.api.support.model.PageModel;
import org.framework.common.api.support.model.QueryModel;
import org.framework.common.ext.util.StringUtil;
import org.springframework.ui.ModelMap;
import org.springframework.util.ReflectionUtils;

/**
 * <p>描述:公共数据访问接口的适配器类，可根据不同的持久化方案有选择的覆盖该类中的接口.</p>
 * <p>Company:上海中信信息发展股份有限公司</p>
 * @author chanlong(陈龙)
 * @date 2013年12月23日  下午2:37:04
 * @version 1.0.2013.
 */
public abstract class CommonDaoAdapter<ENTITY extends CommonEntityAware<ID>, ID extends Serializable> implements CommonDaoAware<ENTITY, ID> {

	/**
	 * @see ces.framework.common.web.dao.CommonDaoAware#insert(ces.framework.common.entity.CommonEntity)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:27:11
	 */
	@Override
	public ENTITY insert(final ENTITY entity) throws Exception {
		return null;
	}

	/**
	 * @see ces.framework.common.web.dao.CommonDaoAware#update(ces.framework.common.entity.CommonEntity)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:27:11
	 */
	@Override
	public ENTITY update(final ENTITY entity) throws Exception {
		return null;
	}

	/**
	 * @see ces.framework.common.web.dao.CommonDaoAware#remove(ces.framework.common.entity.CommonEntity)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:27:11
	 */
	@Override
	public void remove(final ENTITY entity) throws Exception {
	}

	/**
	 * @see ces.framework.common.web.dao.CommonDaoAware#loader(ces.framework.common.entity.CommonEntity)
	 * @author Administrator(陈龙)
	 * @date 2014-1-22 下午2:05:42
	 */
	@Override
	public ENTITY loader(final ENTITY entity) throws Exception {
		return null;
	}
	
	/**
	 * @see ces.framework.common.web.dao.CommonDaoAware#loader(java.lang.Class, java.io.Serializable)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:50:47
	 */
	@Override
	public ENTITY loader(final Class<ENTITY> cls, final ID id) throws Exception {
		return null;
	}
	
	/**
	 * @see org.framework.common.api.service.dao.CommonDaoAware#loader(java.lang.Class, java.util.Map)
	 * @author chanlong(陈龙)
	 * @date 2016年4月9日 下午7:06:19
	 */
	@Override
	public ENTITY loader(final Class<ENTITY> cls, final Map<String, Object> params) throws Exception {
		return null;
	}
	
	/**
	 * @see ces.framework.common.web.dao.CommonDaoAware#loader(java.lang.Class, java.lang.String, java.lang.Object)
	 * @author chanlong(陈龙)
	 * @date 2014年4月25日 下午9:15:52
	 */
	@Override
	public ENTITY loader(final Class<ENTITY> cls, final String field, final Object value) throws Exception {
		return null;
	}
	
	/**
	 * @see org.framework.common.api.service.dao.CommonDaoAware#count(org.framework.common.api.support.model.QueryModel)
	 * @author chanlong(陈龙)
	 * @date 2016年4月13日 下午3:21:55
	 */
	@Override
	public long count(final QueryModel support) throws Exception {
		return 0;
	}

	/**
	 * @see org.framework.common.api.service.dao.CommonDaoAware#findByQuery(org.framework.common.api.support.model.QueryModel)
	 * @author chanlong(陈龙)
	 * @date 2016年1月20日 下午4:04:54
	 */
	@Override
	public PageModel<ENTITY> findByQuery(final QueryModel support) throws Exception {
		return null;
	}
	
	/**
	 * @see ces.framework.common.web.dao.CommonDaoAware#findByQuery(java.lang.Class, java.lang.String)
	 * @author chanlong(陈龙)
	 * @date 2014年3月3日 上午10:59:54
	 */
	@Override
	public List<ENTITY> findByQuery(final Class<ENTITY> cls, final String jpql) throws Exception {
		return null;
	}
	
	/**
	 * @see org.framework.common.api.service.dao.CommonDaoAware#findByQuery(java.lang.Class, org.springframework.ui.ModelMap)
	 * @author chanlong(陈龙)
	 * @date 2016年1月30日 上午10:39:40
	 */
	@Override
	public List<ENTITY> findByQuery(final Class<ENTITY> cls, final String jpql, final ModelMap params) throws Exception {
		return null;
	}

	// 创建条件
	protected TypedQuery<ENTITY> createParams(TypedQuery<ENTITY> query, final ModelMap params){
		if(params != null && !params.isEmpty()){
			Set<String> keys = params.keySet();
			for (String key : keys) {
				String name = StringUtils.split(key,"_")[0];
				Object value = params.get(key);
				query.setParameter(name, value);
			}
		}
		return query;
	}
	
	// 创建条件
	protected Predicate createCondition(final CriteriaBuilder builder, final Root<ENTITY> root, final CriteriaQuery<?> query, final QueryModel support){
		QueryBean[] andQueries = support.getQueryBeans4And();
		QueryBean[] orQueries = support.getQueryBeans4Or();
		String search = support.getSearch();
		
		Criteria<ENTITY> criteria = new Criteria<ENTITY>();
		// and
		if(andQueries != null){
			for (QueryBean bean : andQueries) {
				String operator = bean.getOperator();
				Object 	value 	= bean.getFieldValue();
				Object[] values = bean.getFieldValues();
				
				if((!operator.equals(Operator.IS_NULL.name()) || !operator.equals(Operator.IS_NOT_NULL.name())) 
				 && StringUtil.isNotEmpty(value) || (values != null && values.length > 0)){
					criteria.add(Restrictions.exec(bean), Operator.AND);
				}
			}
		}
		// or
		if(orQueries != null){
			for (QueryBean bean : orQueries) {
				criteria.add(Restrictions.exec(bean), Operator.OR);
			}
		}
		// search
		if(StringUtil.isNotEmpty(search)){
			Class<ENTITY> clazz = support.getClass4Name();
			ReflectionUtils.doWithFields(clazz, this.new FieldCallback(criteria, search));
		}
		
		return criteria.toPredicate(root, query, builder);
	}
	
	// 创建排序
	protected List<Order> createOrder(final CriteriaBuilder builder, final Root<?> root, final QueryModel support){
		OrderBean[] orders = support.getOrderBeans();
		List<Order> expression = new ArrayList<Order>();
		if(orders != null && orders.length > 0){
			for (OrderBean order : orders) {
				expression.add(order.getExpresion(builder, root));
			}
		}
		return expression;
	}
	
	// 创建分组
	protected List<Expression<?>> createGroup(final Root<ENTITY> root, final QueryModel support){
		GroupBean[] groups = support.getGroupBeans();
		List<Expression<?>> expression = new ArrayList<Expression<?>>();
		if(groups != null && groups.length > 0){
			for (GroupBean group : groups) {
				expression.add(group.getExpresion(root));
			}
		}
		return expression;
	}
	
	// 创建查询
	protected TypedQuery<ENTITY> createTypedQuery(final EntityManager em, final CriteriaQuery<ENTITY> query, final QueryModel support){
		int offset = support.getOffset();
		int limit = support.getLimit();
		if(limit == 0){ // 无分页
			return em.createQuery(query);
		}else{
			return em.createQuery(query).setFirstResult(offset).setMaxResults(limit);
		}
	}
	
	// 结果总数
	protected long count(final EntityManager em, final CriteriaBuilder builder, final Class<ENTITY> entityClass, final Predicate condition){
		CriteriaQuery<Long> query = builder.createQuery(Long.class);
		Root<ENTITY> root = query.from(entityClass);
		query.select(builder.count(root)).where(condition);
		return em.createQuery(query).getSingleResult().longValue();
	}
	
	/*
	 * 全字段模糊查询回调
	 * eg:在实体类要模糊查询的字段上增加@Search
	 */
	private class FieldCallback implements org.springframework.util.ReflectionUtils.FieldCallback{

		private Criteria<ENTITY> criteria;
		private String search;
		
		public FieldCallback(Criteria<ENTITY> criteria, final String search) {
			this.criteria = criteria;
			this.search = search;
		}

		@Override
		public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
			Search search = field.getAnnotation(Search.class);
			if(search != null){
				QueryBean bean = new QueryBean();
				bean.setFieldName(field.getName());
				bean.setFieldValue(this.search);
				bean.setOperator(Operator.LIKE.name());
				criteria.add(Restrictions.exec(bean), Operator.OR);
			}
		}
	}
}

